<?php
/**
 *
 * @author    Lucian Daia < lucian.daia@live.com >
 * @copyright 2010, Lucian Daia
 * @package   KChunk
 */
class KChunk_Datasource
{
	/**
	 * @var Zend_Db_Adapter_Pdo_Sqlite
	 */
	protected $_connection;

	/**
	 * Constructor.
	 * Initiates the connection to the database (builds the connection adapter)
	 *
	 * @param string $dataSourcePath
	 */
	public function __construct($dataSourcePath)
	{
		$this->_connection = Zend_Db::factory('Pdo_Sqlite', array(
			'dbname' => $dataSourcePath));
	}

	/**
	 * Creates the necessary tables for the application to work
	 */
	public function createDatabaseStructure()
	{
		// create a download plan table: the table that holds "metadata"
		// about each resource that has to be download.
		$dlPlanTable = "CREATE TABLE download_plans (
		                   resource_url VARCHAR(255),
						   resource_size INT,
						   target_file VARCHAR(255),
						   download_start_time TEXT,
						   download_end_time TEXT,
						   PRIMARY KEY(resource_url)
			            )";
		$this->_connection->query($dlPlanTable);

		// create a chunks table: this table will be populated with all the file
		// segments that need to be downloaded
		$segmentsTable = "CREATE TABLE download_segments (
			                  resource_url VARCHAR(255),
							  start_byte INT,
							  chunk_size MEDIUMINT,
							  download_start_time BIGINT,
							  PRIMARY KEY(resource_url, start_byte)
					      )";
		$this->_connection->query($segmentsTable);
	}

	/**
	 * @return bool
	 */
	public function downloadPlanExists($resourceUrl)
	{
		$query = $this->_connection->select()
				      ->from('download_plans', array('resource_size'))
				      ->where('resource_url = ?', $resourceUrl);
		$stmt  = $this->_connection->query($query);
		return (count($stmt->fetchAll()) > 0) ? true : false;
	}

	/**
	 * Populates the download plans and segments table with data about a resource
	 */
	public function createDownloadPlan($resourceUrl, $totalSize, $targetFile, $chunkSize)
	{
		// we'll wrap the whole thing in a transaction, to make sure that
		// data is not partially written to the database
		try {
			$this->_connection->beginTransaction();
			$this->_connection->insert('download_plans', array(
				'resource_url' => $resourceUrl,
				'resource_size'=> $totalSize,
				'target_file'  => $targetFile,
				'download_start_time' => new Zend_Db_Expr("strftime('%s')")));

			for ($startByte = 0; $startByte < $totalSize;) {
				if ($startByte + $chunkSize < $totalSize) {
					$this->_connection->insert('download_segments', array(
						'resource_url' => $resourceUrl,
						'chunk_size'   => $chunkSize,
						'start_byte'   => $startByte
					));
					$startByte += $chunkSize;
				} else {
					$this->_connection->insert('download_segments', array(
						'resource_url' => $resourceUrl,
						'chunk_size'   => $totalSize - $startByte,
						'start_byte'   => $startByte
					));
					break;
				}
			}
			$this->_connection->commit();
		} catch (Exception $e) {
			echo $e->getMessage();
			$this->_connection->rollBack();
		}
	}

	// gets the next segment that hasn't been started
	public function getNextSegment($url)
	{
		$query = $this->_connection->select()
				      ->from('download_segments', array('start_byte', 'chunk_size'))
				      ->where('resource_url = ?', $url)
		              ->where("(download_start_time < ? OR download_start_time IS NULL)", mktime(0, 0, -20))
				      ->limit(1);
		$stmt  = $this->_connection->query($query);
		return $stmt->fetch();
	}

	public function getTargetFilename($url)
	{
		$query  = $this->_connection->select()
				       ->from('download_plans', array('target_file'))
				       ->where('resource_url = ?', $url);
		$stmt   = $this->_connection->query($query);
		$result = $stmt->fetch();
		return $result['target_file'];
	}


	public function markSegmentAsBegun($resourceUrl, $startByte)
	{
		$time  = time();
		$query = "UPDATE download_segments
			         SET download_start_time = {$time}
			       WHERE resource_url = '{$resourceUrl}'
				         AND start_byte = $startByte";
		$this->_connection->query($query);
	}

	/**
	 * Returns whether all segments for the resource have been downloaded
	 * @param  string $resourceUrl
	 * @return bool
	 */
	public function downloadReady($resourceUrl)
	{
		$query  = "SELECT COUNT(*) AS count FROM download_segments WHERE resource_url = ?";
		$stmt   = $this->_connection->query($query, array($resourceUrl));
		$result = $stmt->fetch();

		return ($result['count'] > 0) ? false : true;
	}

	/**
	 * @param string $resourceUrl
	 */
	public function removePlan($resourceUrl)
	{
		$query = "DELETE FROM download_plans WHERE resource_url = ?";
		$this->_connection->query($query, array($resourceUrl));
	}

	/**
	 * @param string $resourceUrl
	 * @param int    $startByte
	 */
	public function removeSegment($resourceUrl, $startByte)
	{
		$query = "DELETE FROM download_segments
		           WHERE resource_url = ? AND start_byte = ?";
		$this->_connection->query($query, array($resourceUrl, $startByte));
	}
}